package com.hbut.system.service.exam.imp;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollectionUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.github.pagehelper.PageHelper;
import com.hbut.common.core.constants.Constants;
import com.hbut.common.core.enums.ResultCode;
import com.hbut.common.security.exception.ServiceException;
import com.hbut.system.domain.exam.Exam;
import com.hbut.system.domain.exam.ExamQuestion;
import com.hbut.system.domain.exam.dto.ExamAddDTO;
import com.hbut.system.domain.exam.dto.ExamEditDTO;
import com.hbut.system.domain.exam.dto.ExamQueryDTO;
import com.hbut.system.domain.exam.dto.ExamQuestionAddDTO;
import com.hbut.system.domain.exam.vo.ExamDetailVO;
import com.hbut.system.domain.exam.vo.ExamVO;
import com.hbut.system.domain.question.Question;
import com.hbut.system.domain.question.vo.QuestionVO;
import com.hbut.system.manager.ExamCacheManager;
import com.hbut.system.mapper.exam.ExamMapper;
import com.hbut.system.mapper.examQuestion.ExamQuestionMapper;
import com.hbut.system.mapper.question.QuestionMapper;
import com.hbut.system.service.exam.IExamService;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

@Service
@RefreshScope
public class IExamServiceImp extends ServiceImpl<ExamQuestionMapper,ExamQuestion> implements IExamService {
    @Autowired
    private ExamMapper examMapper;

    @Autowired
    private QuestionMapper questionMapper;

    @Autowired
    ExamQuestionMapper examQuestionMapper;

    @Autowired
    ExamCacheManager examCacheManager;


    @Override
    public List<ExamVO> list(ExamQueryDTO questionQueryDTO) {
        PageHelper.startPage(questionQueryDTO.getPageNum(), questionQueryDTO.getPageSize());
        return examMapper.selectExamList(questionQueryDTO);
    }

    @Override
    public int add(ExamAddDTO examAddDTO) {
        //检查参数合法性
        checkExamSaveParams(examAddDTO,null);
        Exam exam = new Exam();
        BeanUtils.copyProperties(examAddDTO, exam);
        return examMapper.insert(exam);
        //todo 修改
//        return exam.getExamId();
    }

    @Override
    public int delete(Long examId) {
        Exam exam = getExam(examId);
        if(exam.getStatus().equals(Constants.TRUE)){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        return examMapper.deleteById(examId) + examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>().eq(ExamQuestion::getExamId, examId));
    }

    @Override
    public boolean questionAdd(ExamQuestionAddDTO examQuestionAddDTO) {
        //检查参数合法性 1.竞赛是否存在 2.添加题目是否存在
        Exam exam = getExam(examQuestionAddDTO.getExamId());
        checkExam(exam);
        Set<Long> questionIdSet = examQuestionAddDTO.getQuestionIdSet();
        if(CollectionUtil.isEmpty(questionIdSet)){
            return true;
        }
        List<Question> questions = questionMapper.selectBatchIds(questionIdSet);
        if(CollectionUtil.isEmpty(questions)||questions.size() != questionIdSet.size()){
            throw new ServiceException(ResultCode.QUESTION_NOT_EXIST);
        }
        return saveExamQuestion(examQuestionAddDTO, questionIdSet);
    }

    @Override
    public int questionDelete(Long examId, Long questionId) {
        //检查参数合法性 1.竞赛是否存在或者已经开始 2.删除的题目是否存在
        Exam exam = getExam(examId);
        checkExam(exam);
        return examQuestionMapper.delete(new LambdaQueryWrapper<ExamQuestion>()
                .eq(ExamQuestion::getExamId, examId)
                .eq(ExamQuestion::getQuestionId, questionId));
    }

    @Override
    public int publish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        Long count = examQuestionMapper.selectCount(new LambdaQueryWrapper<ExamQuestion>()
                        .eq(ExamQuestion::getExamId, examId));
        if(count == 0 || count == null){
            throw new ServiceException(ResultCode.EXAM_QUESTION_NOT_EXIST);
        }
        exam.setStatus(Constants.TRUE);
        examCacheManager.addExamCache(exam);
        return examMapper.updateById(exam);
    }

    @Override
    public int cancelPublish(Long examId) {
        Exam exam = getExam(examId);
        checkExam(exam);
        if(exam.getStatus().equals(Constants.FALSE)){
            throw new ServiceException(ResultCode.EXAM_NOT_PUBLISH);
        }
        exam.setStatus(Constants.TRUE);
        examCacheManager.deleteExamCache(exam.getExamId());
        return examMapper.updateById(exam);
    }


    @Override
    public ExamDetailVO detail(Long examId) {
        //先通过examId获取exam信息，然后通过examId获取questionId，再通过questionId获取question信息，最后返回
        Exam exam = getExam(examId);
        ExamDetailVO examDetailVO = new ExamDetailVO();
        BeanUtils.copyProperties(exam, examDetailVO);
        List<ExamQuestion> examQuestions = examQuestionMapper.selectList(new LambdaQueryWrapper<ExamQuestion>()
                .select(ExamQuestion::getQuestionId)
                .eq(ExamQuestion::getExamId, examId)
                .orderByAsc(ExamQuestion::getQuestionOrder));
        if(CollectionUtil.isEmpty(examQuestions)){
            return examDetailVO;
        }
        List<Long> questionIdList = examQuestions.stream().map(ExamQuestion::getQuestionId).toList();
        List<Question> questions = questionMapper.selectBatchIds(questionIdList);
        List<QuestionVO> questionVOList= BeanUtil.copyToList(questions,QuestionVO.class);
        examDetailVO.setExamQuestionList(questionVOList);
        return examDetailVO;
    }

    @Override
    public int edit(ExamEditDTO examEditDTO) {
        Exam exam = getExam(examEditDTO.getExamId());
        if(exam.getStatus().equals(Constants.TRUE)){
            throw new ServiceException(ResultCode.EXAM_IS_PUBLISH);
        }
        checkExam(exam);
        checkExamSaveParams(examEditDTO, exam.getExamId());
        BeanUtils.copyProperties(examEditDTO, exam);
        return examMapper.updateById(exam);
    }



    private boolean saveExamQuestion(ExamQuestionAddDTO examQuestionAddDTO, Set<Long> questionIdSet) {
        int num = 1;
        List<ExamQuestion> examQuestions = new ArrayList<>();
        for(Long questionId : questionIdSet){
            ExamQuestion examQuestion = new ExamQuestion();
            examQuestion.setExamId(examQuestionAddDTO.getExamId());
            examQuestion.setQuestionId(questionId);
            examQuestions.add(examQuestion);
            examQuestion.setQuestionOrder(num++);
        }
        return saveBatch(examQuestions);
    }

    private static void checkExam(Exam exam) {
        //检查竞赛是否已经开始
        if(exam.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_STARTED);
        }
    }

    private Exam getExam(Long examId) {
        Exam exam = examMapper.selectById(examId);
        if(exam == null){
            throw new ServiceException(ResultCode.FAILED_NOT_EXISTS);
        }
        return exam;
    }

    private void checkExamSaveParams(ExamAddDTO examAddDTO,Long examId) {
        List<Exam> exams = examMapper
                .selectList(new LambdaQueryWrapper<Exam>()
                        .eq(Exam::getTitle, examAddDTO.getTitle())
                        .ne(examId!=null,Exam::getExamId,examId));
        if(CollectionUtil.isNotEmpty(exams)){
            throw new ServiceException(ResultCode.FAILED_ALREADY_EXISTS);
        }
        if(examAddDTO.getStartTime().isBefore(LocalDateTime.now())){
            throw new ServiceException(ResultCode.EXAM_START_TIME_BEFORE_CURRENT_TIME);
        }
        if (examAddDTO.getStartTime().isAfter(examAddDTO.getEndTime())) {
            throw new ServiceException(ResultCode.EXAM_START_TIME_AFTER_END_TIME);
        }
    }
}
